/* midi kaosslator */

#define ROTARY_BEAT (0)
#define ROTARY_SCALE (3)

void shortMessage(char status, char data1, char data2) {
  Serial.print(status, BYTE);
  Serial.print(data1, BYTE);
  Serial.print(data2, BYTE);
}

void noteOn(char note, char vel) {
  shortMessage(0x90, note, vel);
}

void noteOff(char note) {
  shortMessage(0x80, note, 0);
}

void ctrl(char no, char val) {
  shortMessage(0xb0, no, val);
}

int getRotary(int r) {
  // r : ROTARY_BEAT or ROTARY_SCALE
  int raw = (digitalRead(r + 4) == HIGH ? 1 : 0) * 4
          + (digitalRead(r + 3) == HIGH ? 1 : 0) * 2
          + (digitalRead(r + 2) == HIGH ? 1 : 0);
  //ctrl(1, raw);
  return raw - (raw < 4 ? 1 : 2);
}

int readY() {
  pinMode(17, INPUT);
  pinMode(15, INPUT);
  digitalWrite(17, HIGH);
  digitalWrite(15, HIGH);
  pinMode(14, OUTPUT);
  pinMode(16, OUTPUT);
  digitalWrite(14, HIGH);
  digitalWrite(16, LOW);
  //delay(1);
  return analogRead(1);
}

int readX() {
  pinMode(16, INPUT);
  pinMode(14, INPUT);
  digitalWrite(16, HIGH);
  digitalWrite(14, HIGH);
  pinMode(17, OUTPUT);
  pinMode(15, OUTPUT);
  digitalWrite(17, HIGH);
  digitalWrite(15, LOW);
  //delay(1);
  return analogRead(2);
}

void setup() {
  for (int pin = 2; pin <= 7; pin++) {
    pinMode(pin, INPUT);
    digitalWrite(pin, HIGH);
  }
  Serial.begin(31250);
  shortMessage(0xb0, 0x78, 0);
  pinMode(8, OUTPUT);  
}

int scaleNum[6] = {15, 11, 11, 11, 11, 7};
char scaleNotes[6][25] = {
  {48, 50, 52, 53, 55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72}, // Ionian
  {48, 50, 52, 55, 57, 60, 62, 64, 67, 69, 72}, // Major Pentatonic
  {48, 51, 53, 55, 58, 60, 63, 65, 67, 70, 72}, // Minor Pentatonic
  {48, 52, 53, 55, 59, 60, 64, 65, 67, 71, 72}, // Ryukyu
  {48, 50, 53, 55, 58, 60, 62, 65, 67, 70, 72}, // Egypt
  {48, 53, 58, 60, 65, 70, 72}, // 4th Interval
};

int x2pitch(int x) {
  if (x > 950) return -1;
  int scale = getRotary(ROTARY_SCALE);
  if (x > 850) return scaleNotes[scale][scaleNum[scale] - 1];
  int p = map(x, 180, 850, 0, scaleNum[scale] - 1);
  return scaleNotes[scale][p];
}

int y2velocity(int y) {
  return map(y, 140, 900, 30, 127);
}

int sequence[6][16] = {
  {23 ,24, 35, 36, 47, 48, 59, 60, 71, 72, 83, 84, 95, 0}, // stroke 1
  {23 ,24, 35, 36, 59, 60, 71, 72, 83, 84, 95, 0}, // stroke 2
  {24, 36, 46, 48, 72, 84, 94, 0}, // bass line
  {11, 12, 24, 36, 47, 48, 59, 60, 72, 84, 95, 0}, // 8 beat
  {15, 16, 23, 24, 39, 40, 47, 48, 63, 64, 71, 72, 87, 88, 95, 0}, // triplet 1
  {24, 40, 47, 48, 71, 72, 79, 80, 95, 0} // triplet 2
};
int seqp = 0;
boolean playing = true;

void handleMidiClock(int beat) {
  static boolean on = false;
  static char pitch;
  static int counter = 0;
  int newPitch = x2pitch(readX());
  if (!on) {
    if (newPitch >= 0) {
      pitch = newPitch;
      noteOn(pitch, y2velocity(readY()));
      digitalWrite(8, HIGH);
      counter = 0;
      seqp = 0;
      playing = true;
      on = true;
    }
  } else {
    counter = (counter + 1) % 96;
    if (newPitch >= 0) {
      if (counter == sequence[beat][seqp]) {
        if (playing) {
          noteOff(pitch);
          digitalWrite(8, LOW);
        } else {
          pitch = newPitch;
          noteOn(pitch, y2velocity(readY()));
          digitalWrite(8, HIGH);
        }
        playing = !playing;
        seqp = sequence[beat][seqp] == 0 ? 0 : seqp + 1;  
      }
    } else {
      noteOff(pitch);
      on = false;
      digitalWrite(8, LOW);
    }
  }
}

void displayRotary() {
  ctrl(1, getRotary(ROTARY_BEAT));
  ctrl(2, getRotary(ROTARY_SCALE));
  delay(1000);
}

void loop () {
  static int last = 0;
  static int lastx = 0;
  static int lasty = 0;

  static boolean on = false;
  static char pitch;
  static int transient = -1;

  //displayRotary(); return;
  
  /*while (1) {
    ctrl(1, map(readX(), 0, 1024, 0, 127));
    ctrl(2, map(readY(), 0, 1024, 0, 127));
    delay(1000);
  }*/
  
  int beat = getRotary(ROTARY_BEAT);
  if (beat != 0) {
    while (Serial.available() > 0)
      if (Serial.read() == 0xf8) handleMidiClock(beat-1);
    return;
  }

  int newPitch = x2pitch(readX());
  if (transient == newPitch) {
    //ctrl(1,0);
    if (!on) {
      if (newPitch >= 0) {
        ctrl(2,0);
        noteOn(pitch, y2velocity(readY()));
        on = true;
        digitalWrite(8, HIGH);   
      }
    } else {
      if (newPitch < 0) {
        noteOff(pitch);
        on = false;
        digitalWrite(8, LOW);  
      } else {
        if (newPitch != pitch) {
          noteOff(pitch);
          pitch = newPitch;
          noteOn(pitch, y2velocity(readY()));
        }
      }
    }
  } else {
    transient = newPitch;
  }
}
